package com.zs.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ZcApplicationContext {
    private Class configClass;
    private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //单力池
    private static Map<String, Object> singletonMap = new ConcurrentHashMap<>();
    //beanpostproccesser池
    private List<BeanPostProccesser> postProccessers = new ArrayList<>();

    public ZcApplicationContext(Class clazz) throws ClassNotFoundException {
        this.configClass = clazz;
        //加载扫描文件下的类
        List<Class> classList = loadClass();
        //解析类的注解定义信息
        initBeanDefinition(classList);
        //创建实例
        initBeanInstance();
    }

    private void initBeanInstance() {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                if (!singletonMap.containsKey(beanName)) {
                    Class aClass = beanDefinition.getaClass();

                    Object o = doCreateBean(beanName, beanDefinition);
                    singletonMap.put(beanName, o);


                }
            }
        }
    }

    private void initBeanDefinition(List<Class> classList) {
        if (classList.size() > 0) {
            //创建实例定义
            for (Class aClass : classList) {
                if (BeanPostProccesser.class.isAssignableFrom(aClass)) {
                    try {
                        BeanPostProccesser o = (BeanPostProccesser) aClass.getDeclaredConstructor().newInstance();
                        postProccessers.add(o);
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }
                Component annotation = (Component) aClass.getAnnotation(Component.class);
                String beanName = annotation.value();
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setaClass(aClass);
                beanDefinition.setBeanName(beanName);
                if (aClass.isAnnotationPresent(Scope.class)) {
                    Scope scope = (Scope) aClass.getAnnotation(Scope.class);
                    String value = scope.value();
                    beanDefinition.setScope(value);
                } else {
                    beanDefinition.setScope("singleton");
                }
                beanDefinitionMap.put(beanName, beanDefinition);
            }
        }
    }

    private List<Class> loadClass() throws ClassNotFoundException {
        List<Class> classList = new ArrayList<>();
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan annotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String value = annotation.value();
            String replace = value.replace(".", "/");
            ClassLoader classLoader = configClass.getClassLoader();
            URL resource = classLoader.getResource(replace);
            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File file1 : files) {
                    String beanPath = value+ "." + file1.getName().substring(0,file1.getName().lastIndexOf("."));
                    Class<?> aClass = classLoader.loadClass(beanPath);
                    if (aClass.isAnnotationPresent(Component.class)) {
                        classList.add(aClass);
                    }
                }

            }

        }
        return classList;
    }

    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")) {
            Object o = singletonMap.get(beanDefinition.getBeanName());
            if (o == null) {
                o = doCreateBean(beanName,beanDefinition);
                singletonMap.put(beanName, o);
            }
            return o;
        } else {
            Class aClass = beanDefinition.getaClass();
            Object o = null;
            try {
                o = aClass.getDeclaredConstructor().newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return o;
        }
    }

    private Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
        Object o = null;
        try {
            //1.实例化
            Class aClass = beanDefinition.getaClass();
            o = aClass.getDeclaredConstructor().newInstance();
            //2.属性舒服
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    String name = field.getName();
                    Object bean = getBean(name);
                    field.setAccessible(true);
                    field.set(o,bean);
                } else if (field.isAnnotationPresent(Value.class)) {
                    Value annotation = field.getAnnotation(Value.class);
                    String value = annotation.value();
                    field.setAccessible(true);
                    field.set(o,value);
                }
            }

            for (BeanPostProccesser postProccesser : postProccessers) {
                postProccesser.postProcessBeforeInitialization(o,beanName);
            }


            //判断是否实现BeanAwre
            if (o instanceof BeanNameAware) {
                ((BeanNameAware) o).set("yyyy");
            }
            for (BeanPostProccesser postProccesser : postProccessers) {
                postProccesser.postProcessAfterInitialization(o,beanName);
            }


        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return o;
    }

}
